// Copyright (c) Microsoft Corporation. 
// Licensed under the MIT license.

#pragma once

#include "CoreMinimal.h"
#include "ISimplygonReduction.h"

class FSkeletalMeshModel;

class FSimplygonRunnable : public FRunnable
{
public:
	bool IsDone;
	FRunnableThread* Thread;
};

class FSimplygonReduction : public ISimplygonReduction
{
public:
	FSimplygonReduction(bool bUseDistributedMeshMerging = false);
	~FSimplygonReduction();

	virtual bool Tick(float DeltaTime) override;

	/** Begin IMeshReduction interface*/
	virtual void ReduceMeshDescription(FMeshDescription& OutReducedMesh, 
		float& OutMaxDeviation,
		const FMeshDescription& InMesh,
		const FOverlappingCorners& InOverlappingCorners,
		const FMeshReductionSettings& ReductionSettings) override;

	virtual bool ReduceSkeletalMesh(class USkeletalMesh* SkeletalMesh, int32 LODIndex, const class ITargetPlatform* TargetPlatform) override;

	virtual const FString& GetVersionString() const override;

	virtual bool IsSupported() const override;

	virtual bool IsReductionActive(const struct FMeshReductionSettings& ReductionSettings) const override;
	virtual bool IsReductionActive(const struct FSkeletalMeshOptimizationSettings& ReductionSettings) const override;
	virtual bool IsReductionActive(const struct FSkeletalMeshOptimizationSettings& ReductionSettings, uint32 NumVertices, uint32 NumTriangles) const override;
	/** End IMeshReduction interface*/

	/** Begin IMeshMerging interface*/
	virtual void ProxyLOD(const TArray<FMeshMergeData>& InData,
		const FMeshProxySettings& InProxySettings,
		const TArray<FFlattenMaterial>& InputMaterials,
		const FGuid InJobGUID) override;

	virtual FString GetName() override;
	/** End IMeshMerging interface*/

	/** Begin ISimplygonReduction interface*/
	virtual void RunSceneAsync(Simplygon::spScene SceneToProcess,
		Simplygon::spPipeline Pipeline,
		const FSimplygonJobData& JobData,
		const FSimplygonProcessProgressDelegate& ProgressDelegate,
		const FSimplygonProcessCompleteDelegate& CompleteDelegate,
		const FSimplygonProcessFailedDelegate& FailedDelegate) override;

	void RunSceneSync(FSimplygonJobData& JobData,
		Simplygon::spScene Scene,
		Simplygon::spPipeline Pipeline,
		const FSimplygonProcessProgressDelegate& ProgressDelegate,
		const FSimplygonProcessCompleteDelegate& CompleteDelegate,
		const FSimplygonProcessFailedDelegate& FailedDelegate);

	virtual void RunBatchAsync(Simplygon::spPipelineBatch Batch,
		const FSimplygonJobData& JobData,
		const FSimplygonProcessProgressDelegate& ProgressDelegate,
		const FSimplygonProcessBatchCompleteDelegate& CompleteDelegate,
		const FSimplygonProcessFailedDelegate& FailedDelegate) override;

	void RunBatchSync(const FSimplygonJobData& JobData, 
		Simplygon::spPipelineBatch Batch,
		const FSimplygonProcessProgressDelegate& ProgressDelegate,
		const FSimplygonProcessBatchCompleteDelegate& CompleteDelegate,
		const FSimplygonProcessFailedDelegate& FailedDelegate);
	/** End ISimplygonReduction interface*/

	virtual FSimplygonUpdateNavMeshVisibilitySamplesDelegate GetNavmeshVisibilitySamplerUpdateHandler() override
	{
		return VisibilitySamplesHandler;
	}

	virtual TArray<FVector> GetNavMeshVisibilitySamples() const override { return VisibilitySamplePoints; }

	virtual FSimplygonUpdateCuttingPlanesDelegate GetCuttingPlaneUpdateHandler() override
	{
		return CuttingPlanesHandler;
	}

	virtual TArray<FPlane> GetClippingPlanes() const override { return CuttingPlanes; }

	virtual void ClearNavmeshSamples() override 
	{
		VisibilitySamplePoints.Empty();
	}
	
	virtual void ClearClippingPlane() override
	{
		CuttingPlanes.Empty();
	}

	bool IsUsingDistribution() const override;
	bool IsUsingIncrediBuild() const override;

	virtual bool IsUsingDistributedMeshMerging() const override { return bAllowDistributedMeshMerging; };

private:
	void ReduceSkeletalMeshWithResource(Simplygon::ISimplygon* SDK, USkeletalMesh* SkeletalMesh,
		FSkeletalMeshModel* SkeletalMeshResource, int32 LODIndex);

	bool ReduceSkeletalLODModel(
		Simplygon::ISimplygon* SDK,
		FSkeletalMeshLODModel* SrcModel,
		FSkeletalMeshLODModel*& OutModel,
		USkeletalMesh* SkeletalMesh,
		float& MaxDeviation,
		const FSkeletalMeshOptimizationSettings& MeshOptimizationSettings,
		const TArray<FMatrix>& BoneMatrices,
		const int32 LODIndex);

	bool RemoveSkeletalMeshSection(FSkeletalMeshLODModel* Model, int32 SectionIndex);

	void RunPipeline(Simplygon::ISimplygon* SDK, 
		Simplygon::spPipeline Pipeline, 
		Simplygon::spScene Scene,
		bool AllowDistribution,
		const FSimplygonJobData& JobData = FSimplygonJobData(),
		const FSimplygonProcessProgressDelegate& ProgressDelegate = FSimplygonProcessProgressDelegate());

	void RunPipelineBatch(Simplygon::ISimplygon* SDK, 
		Simplygon::spPipelineBatch Batch, 
		bool AllowDistribution,
		const FSimplygonJobData& JobData = FSimplygonJobData(),
		const FSimplygonProcessProgressDelegate& ProgressDelegate = FSimplygonProcessProgressDelegate());

	void SetReductionSettings(Simplygon::spReductionSettings ReductionSettings,
		const FMeshReductionSettings& Settings, int32 NumTris);

	void SetNormalSettings(Simplygon::spNormalCalculationSettings NormalSettings,
		const FMeshReductionSettings& Settings);

	void SetVisibilitySettings(Simplygon::spVisibilitySettings VisibiliySettings);
	
	void SetSkeletalReductionSettings(const FSkeletalMeshOptimizationSettings& Settings,
		float BoundsRadius, int32 SourceTriCount,
		Simplygon::spReductionSettings ReductionSettings);

	void SetSkeletalNormalSettings(const FSkeletalMeshOptimizationSettings& Settings,
		Simplygon::spNormalCalculationSettings NormalSettings);

	void SetBoneSettings(const FSkeletalMeshOptimizationSettings& Settings,
		Simplygon::spBoneSettings BoneSettings);

	bool ValidateGeometry(Simplygon::ISimplygon* SDK,
		Simplygon::spGeometryValidator& GeometryValidator,
		Simplygon::spGeometryData& GeometryData);

private:
	FString VersionString;
	FCriticalSection StateLock;
	FQueuedThreadPool* SimplygonTaskPool;
	FSimplygonUpdateNavMeshVisibilitySamplesDelegate VisibilitySamplesHandler;
	FSimplygonUpdateCuttingPlanesDelegate CuttingPlanesHandler;
	TArray<FVector> VisibilitySamplePoints;
	TArray<FPlane> CuttingPlanes;

	bool bAllowDistributedMeshMerging;
};
